﻿using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Infra_Common.redis
{
    //用于连接Redis服务器，包括创建连接，获取数据库等操作
    public class RedisClient : IDisposable
    {
        #region 连接配置
        private string _connectionString;
        private string _instanceName;
        private int _defaultDB;
        private ConcurrentDictionary<string, ConnectionMultiplexer> _connections;
        public RedisClient(string connectionString, string instanceName, int defaultDB = 0)
        {
            _connectionString = connectionString;
            _instanceName = instanceName;
            _defaultDB = defaultDB;
            _connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
        }

        private ConnectionMultiplexer GetConnect()
        {
            return _connections.GetOrAdd(_instanceName, p => ConnectionMultiplexer.Connect(_connectionString));
        }

        public IDatabase GetDatabase()
        {
            return GetConnect().GetDatabase(_defaultDB);
        }

        public IServer GetServer(string configName = null, int endPointsIndex = 0)
        {
            var confOption = ConfigurationOptions.Parse(_connectionString);
            return GetConnect().GetServer(confOption.EndPoints[endPointsIndex]);
        }

        public ISubscriber GetSubscriber(string configName = null)
        {
            return GetConnect().GetSubscriber();
        }
        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="subChannel">"__keyevent@0__:expired"</param>
        /// <param name="func"></param>
        public async Task Sub(string subChannel,Func<RedisValue,bool> func)
        {
            this.GetSubscriber().Subscribe(subChannel, (channel, message) =>
            {
                func(message);
            });
        }
        /// <summary>
        /// Redis发布订阅  取消订阅
        /// </summary>
        /// <param name="channel"></param>
        public void Unsubscribe(string channel)
        {
            this.GetSubscriber().Unsubscribe(channel);
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Dispose()
        {
            if (_connections != null && _connections.Count > 0)
            {
                foreach (var item in _connections.Values)
                {
                    item.Close();
                }
            }
        }
        #endregion

        #region  异步方法
        /// <summary>
        /// 根据Key移除
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<bool> Remove(string key)
        {
            var db = this.GetDatabase();

            return await db.KeyDeleteAsync(key);
        }

        /// <summary>
        /// 根据key获取string结果
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<string> Get(string key)
        {
            var db = this.GetDatabase();
            return await db.StringGetAsync(key);
        }

        /// <summary>
        /// 根据key获取string中的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> Get<T>(string key)
        {
            var t = default(T);
            try
            {
                var _str = await this.Get(key);
                if (string.IsNullOrWhiteSpace(_str)) { return t; }

                t = JsonConvert.DeserializeObject<T>(_str);
            }
            catch (Exception ex) { }
            return t;
        }

        /// <summary>
        /// 存储string数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expireMinutes"></param>
        /// <returns></returns>
        public async Task<bool> Set(string key, string value, int expireMinutes = 0)
        {
            var db = this.GetDatabase();
            if (expireMinutes > 0)
            {
                return db.StringSet(key, value, TimeSpan.FromMinutes(expireMinutes));
            }
            return await db.StringSetAsync(key, value);
        }

        /// <summary>
        /// 存储对象数据到string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expireMinutes"></param>
        /// <returns></returns>
        public async Task<bool> Set<T>(string key, T value, int expireMinutes = 0)
        {
            try
            {
                var jsonOption = new JsonSerializerSettings()
                {
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                };
                var _str = JsonConvert.SerializeObject(value, jsonOption);
                if (string.IsNullOrWhiteSpace(_str)) { return false; }

                return await this.Set(key, _str, expireMinutes);
            }
            catch (Exception ex) { }
            return false;
        }

        /// <summary>
        /// 是否存在key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<bool> KeyExists(string key)
        {
            try
            {
                var db = this.GetDatabase();
                return await db.KeyExistsAsync(key);
            }
            catch (Exception ex) { }
            return false;
        }
        /// <summary>
        /// 得到多个value，并且修改他的key,防止同时被其他管理员读取
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keys"></param>
        /// <returns></returns>
        public async Task<List<T>> GetAny<T>(params string[] keys)
        {
            var db = this.GetDatabase();
            var t = default(T);
            List<T> t_vlaue = new List<T>();
            try
            {
                var newkeys = ConvertRedisKeysAddSysCustomKey(keys);
                var values = await db.StringGetAsync(newkeys);
                foreach (var value in values)
                {
                    t = JsonConvert.DeserializeObject<T>(value);
                    t_vlaue.Add(t);
                }

            }
            catch (Exception ex) { }
            return t_vlaue;
        }
        /// <summary>
        /// 随机获得几个redis值
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public async Task<List<string>> GetAnyKey(int num, string keyPrefix)
        {

            var Service = this.GetServer();//默认一个redis服务器
            List<string> keys = new List<string>();//, pattern: keyPrefix + @"\*")
            foreach (var key in Service.Keys(database: 0))
            {
                if (string.IsNullOrWhiteSpace(key) && keys.Count <= num)
                {
                    keys.Add(key);

                }
                else
                {
                    break;
                }
            }
            return keys;
        }
        /// <summary>
        /// 将key重命名
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public async Task<bool> Updatekey(List<string> keys)
        {
            var db = this.GetDatabase();
            try
            {
                foreach (var key in keys)
                {
                    RedisKey oldkey = (RedisKey)key;
                    RedisKey newkey = (RedisKey)AddSysCustomKey(key);

                    db.KeyRename(oldkey, newkey);
                }
                return true;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
         }
        #endregion

        #region  同步方法
        /// <summary>
        /// 随机获得几个redis值
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public List<string> GetAnyKeySyn(int num, string keyPrefix)
        {

            //var Service = this.GetServer();//
            var db = this.GetDatabase();
            var p = "^" + keyPrefix + @"[\S]{4,200}";
            List<string> keys = new List<string>();
            //foreach (var key in Service.Keys(database: 0))
            while(num>0)
            {
                string key = db.KeyRandom();
                if (!string.IsNullOrWhiteSpace(key) && keys.Count <= num)
                {
                    if (!key.Contains("onlock"))
                    {
                        keys.Add(key);
                        num--;
                    }
                }
                else
                {
                    break;
                }

            }
            return keys;
        }
        public bool UpdatekeySyn(List<string> keys)
        {
            var db = this.GetDatabase();
            try
            {
                foreach (var key in keys)
                {
                    RedisKey oldkey = (RedisKey)key;
                    RedisKey newkey = (RedisKey)AddSysCustomKey(key);

                    db.KeyRename(oldkey, newkey);
                }
                return true;
            }
            catch (Exception ex)
            {
                throw (ex);
            }

        }
        #endregion

        #region 辅助方法
        /// <summary>
        /// 自定义Key前缀,表示不能被读取
        /// </summary>
        protected string CustomKey = "onlock";
        /// <summary>
        /// 设置前缀
        /// </summary>
        /// <param name="customKey"></param>
        public void SetSysCustomKey(string customKey) => CustomKey = customKey;
        /// <summary>
        /// 组合缓存Key名称
        /// </summary>
        /// <param name="oldKey"></param>
        /// <returns></returns>
        public string AddSysCustomKey(string oldKey) => $"{CustomKey}_{oldKey}";
        public string RemoveSysCustomKey(string oldKey)
        {
            int s = oldKey.IndexOf("_");//索引从0开始
            return oldKey.Substring(s, oldKey.Length - s);
        }
        /// <summary>
        /// 将对象转换成string字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        protected string ConvertJson<T>(T value)
        {
            string result = value is string ? value.ToString() :
                JsonConvert.SerializeObject(value, Formatting.None);
            return result;
        }
        /// <summary>
        /// 将string类型的Key转换成 <see cref="RedisKey"/> 型的Key
        /// </summary>
        /// <param name="redisKeys"></param>
        /// <returns></returns>
        protected RedisKey[] ConvertRedisKeys(params string[] redisKeys) => redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
        /// <summary>
        /// 将string类型的Key转换成 <see cref="RedisKey"/> 型的Key，并添加前缀字符串
        /// </summary>
        /// <param name="redisKeys"></param>
        /// <returns></returns>
        protected RedisKey[] ConvertRedisKeysAddSysCustomKey(params string[] redisKeys) => redisKeys.Select(redisKey => (RedisKey)AddSysCustomKey(redisKey)).ToArray();
        /// <summary>
        /// 去掉添加的前缀
        /// </summary>
        /// <param name="redisKeys"></param>
        /// <returns></returns>
        protected RedisKey[] ConvertRedisKeysRemoveSysCustomKey(params string[] redisKeys)=> redisKeys.Select(redisKey => (RedisKey)RemoveSysCustomKey(redisKey)).ToArray();
        /// <summary>
        /// 将值集合转换成RedisValue集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisValues"></param>
        /// <returns></returns>
        protected RedisValue[] ConvertRedisValue<T>(params T[] redisValues) => redisValues.Select(o => (RedisValue)ConvertJson<T>(o)).ToArray();
        #endregion
    }
}
